package com.pinfly.common.crypto;

import java.io.IOException;
import java.math.BigInteger;
import java.security.interfaces.RSAPublicKey;

import com.pinfly.common.util.HexConvert;

/**
 * Represents an X.509 RSA PKCS public key. The constructors in this class
 * assume that the keyData that is being passed has the following ASN.1
 * structure, and was DER encoded: <CODE><PRE>
 * SubjectPublicKeyInfo ::= SEQUENCE {
 *  algorithm AlgorithmIdentifier,
 *  subjectPublicKey BIT STRING }
 * 
 * Where AlgorithmIdentifier is defined as:
 *   AlgorithmIdentifier ::= SEQUENCE {
 *      algorithm OBJECT IDENTIFIER,
 *      parameters ANY DEFINED BY algorithm OPTIONAL }
 * 
 * And the subjectPublicKey BIT STRING is defined to wrap the following:
 *   RSAPublicKey ::= SEQUENCE {
 *     modulus           INTEGER,  -- n
 *     publicExponent    INTEGER   -- e }
 * </PRE></CODE><BR/>
 * 
 * Note: For additional information see the X.509 specification (rfc 3280),
 * "A Layman's Guide to a Subset of ASN.1, BER, and DER" by Burton S. Kaliski
 * Jr, and the PKCS-1 standard found on www.rsa.com.<BR/>
 * <BR/>
 * 
 * Below is an example of an X.509 RSA public key structure for a 512 bit key:<BR/>
 * <BR/>
 * 
 * 1) First the code that was used to produce the key: <CODE><PRE>
 *  KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
 *  kpg.initialize(512, new SecureRandom());
 *  KeyPair kp = kpg.genKeyPair();
 *  RSAPublicKey pk = (RSAPublicKey)kp.getPublic();
 *  String pkAsHexString = HexConvert.convertHexString(pk);
 *  </PRE></CODE><BR/>
 * 
 * 2) The hex key, and analysis: <CODE><PRE>
 *  pkAsHexString = "305c300d06092a864886f70d0101010500034b003048024100e9199bbb
 *    37dfb52bdd4511a00d8a7954092b927155e0d37e3cdcf492a7b6416cd85e211f22ad83e99
 *    c981890a0f3c1b0d8ad939bb811a282238ed1d4fba1149d0203010001"
 *    
 *  30 -    00       - Universal type
 *          1        - Structured
 *          10000    - 16 - SEQUENCE
 *  5C -    0        - Short length form
 *          1011100  - Length 92 octets = 184 hex
 * 
 *  AlgorithmIdentifier ::= SEQUENCE {
 *     algorithm OBJECT IDENTIFIER,
 *     parameters ANY DEFINED BY algorithm OPTIONAL }
 * 
 *  30 -    00       - Universal type
 *          1        - Structured
 *          10000    - 16 - SEQUENCE
 *  0D -    0        - Short length form
 *          0001101  - 13 octets = 26 hex
 *  
 *  rsaEncryption OBJECT IDENTIFIER ::= { iso(1) member-body(2)
 *    us(840) rsadsi(113549) pkcs(1) 1 1 }
 *                
 *  06 -    00000110 - OBJECT IDENTIFIER
 *  09 -    0        - Short length form
 *          0001001  - Length 9 octets = 18 hex
 *  2A -     1, 2    - iso and member-body
 *  8648 -   840     - us
 *  86f70d - 113549  - rsadsi
 *  01 -     1       - pkcs
 *  01 -     1       
 *  01 -     1
 * 
 *  parameters ANY DEFINED BY algorithm OPTIONAL
 *  
 *  0500 -   NULL   - No parameters
 * 
 *  subjectPublicKey BIT STRING  
 *     
 *  03 -    00      - Universal type
 *          0       - primitive
 *          00011   - BIT String
 *  4b -    0       - Short length
 *          1001011 - 75 octets = 150 hex
 *  00 -    0       - Unused bits
 * 
 *     RSAPublicKey ::= SEQUENCE {
 *       modulus INTEGER, -- n
 *       publicExponent INTEGER -- e }
 * 
 *  30 -    00      - Universal type
 *          1       - Structured
 *          10000   - 16 - Sequence
 *  48 -    0       - Short length form
 *          1001000 - 72 octets = 144 hex
 * 
 *  Modulus
 *  02 -    00      - Universal type
 *          0       - Primitive
 *          00010   - Integer
 *  41 -    0       - Short length form
 *          1000001 - 65 octets = 130 hex
 * 
 *  (base 256)
 *  00e9199bbb37dfb52bdd4511a00d8a7954092b927155e0d37e3cdcf492a7b
 *  6416cd85e211f22ad83e99c981890a0f3c1b0d8ad939bb811a282238ed1d4
 *  fba1149d
 * 
 *  Public exponent
 *  02 -    00      - Universal type
 *          0       - Primitive
 *          00010   - Integer
 *  03 -    0       - Simple length form
 *          0000011 - 3 octets = 6 hex
 * 
 *  (base 256)
 *  010001 = 1 00 1 = 1 * 256^2 + 0 * 256^1 + 1 * 256^0 = 65536 + 0 + 1 = 65537    
 *  </PRE></CODE>
 */
public class X509RSAPublicKey
{
    private byte[] m_algorithmId;
    private BigInteger m_exponent;
    private BigInteger m_modulus;

    /**
     * Creates a new instance
     */
    public X509RSAPublicKey ()
    {
    }

    /**
     * Creates a new instance given an RSAPublicKey
     * 
     * @param key RSAPublicKey
     */
    public X509RSAPublicKey (RSAPublicKey key)
    {
        setExponent (key.getPublicExponent ());
        setModulus (key.getModulus ());
    }

    /**
     * Creates a new instance given a Microsoft RSA Public key
     * 
     * @param msk The Microsoft public key
     */
    public X509RSAPublicKey (MSRSAPublicKey msk)
    {
        setExponent (msk.getExponent ());
        setModulus (msk.getModulus ());
    }

    /**
     * Creates a new instance with the passed in X.509 PKCS DER encoded key.
     * 
     * @param keyData The hex reprensentation of an X.509 PKCS DER encoded key
     * @throws IOException if the passed in key is not in the correct format
     */
    public X509RSAPublicKey (String keyData) throws IOException
    {
        load (HexConvert.convertToBytes (keyData));
    }

    /**
     * Creates a new instance with the passed in X.509 PKCS DER encoded key.
     * 
     * @param keyData The byte array that contains the X.509 key
     * @throws IOException if the passed in key is not in the correct format
     */
    public X509RSAPublicKey (byte[] keyData) throws IOException
    {
        load (keyData);
    }

    /**
     * Gets the exponent
     * 
     * @return The RSA exponent
     */
    public BigInteger getExponent ()
    {
        return m_exponent;
    }

    /**
     * Sets the exponent
     * 
     * @param exp The exponent
     */
    public void setExponent (BigInteger exp)
    {
        m_exponent = exp;
    }

    /**
     * Sets the exponent
     * 
     * @param exp The exponent
     */
    public void setExponent (int exp)
    {
        m_exponent = BigInteger.valueOf (exp);
    }

    /**
     * Gets the modulus
     * 
     * @return the RSA modulus.
     */
    public BigInteger getModulus ()
    {
        return m_modulus;
    }

    /**
     * Sets the modulus
     * 
     * @param mod The RSA modulus
     */
    public void setModulus (BigInteger mod)
    {
        m_modulus = mod;
    }

    private void load (byte[] keyData) throws IOException
    {
        // Read the input stream using Big-endian
        KeyInputStream stream = new KeyInputStream (keyData, false);

        // The key data is wrapped in a sequence, forward to its
        // contents.
        X509Util.forwardToSequenceContents (stream);

        // Get the algorithm identifier
        m_algorithmId = X509Util.readAlgorithmIdentifier (stream);

        // Verify that the key uses RSA PKCS
        if (!X509Util.isRSAAlgorithmIdentifier (m_algorithmId))
        {
            String alg = HexConvert.convertHexString (m_algorithmId);
            throw new IOException ("Algorithm Identifier read is not an RSA PKCS identifier!" + "\nExpected: "
                                   + X509Util.RSA_ALGORITHM_IDENTIFIER + "\nRead: " + alg);
        }

        // The modulus and exponent are wrapped in a Sequence, which is
        // wrapped in a BitString
        X509Util.forwardToBitStringContents (stream);
        X509Util.forwardToSequenceContents (stream);

        // Get the Exponent and Modulus
        m_modulus = X509Util.readInteger (stream);
        m_exponent = X509Util.readInteger (stream);

    }

    /**
     * Gets this public RSA key as a byte array, DER encoded
     * 
     * @return The byte array that represents this key.
     */
    public byte[] getEncoded ()
    {
        byte[] modulusData = X509Util.createInteger (m_modulus);
        byte[] exponentData = X509Util.createInteger (m_exponent);

        // Combine these two
        byte[] data = KeyUtil.appendToArray (modulusData, null);
        data = KeyUtil.appendToArray (exponentData, data);

        // Wrap the exponent and modulus in a SEQUENCE then a BITSTRING
        data = X509Util.createSequence (data);
        data = X509Util.createBitString (data);

        // Add the RSA Algorithm identifier
        data = KeyUtil.prependToArray (X509Util.createRSAAlgorithmIdentifier (), data);

        // Wrap the whole thing in a SEQUENCE
        data = X509Util.createSequence (data);

        return data;
    }

    /**
     * Gets this public RSA key as a DER encoded hex string
     * 
     * @return The hex representation of this key
     */
    public String getEncodedAsString ()
    {
        return (HexConvert.convertHexString (getEncoded ()));
    }

    public String toString ()
    {
        return ("*** X509RSAPublicKey ***" + "\nExponent: " + m_exponent + "\nModulus: " + m_modulus);
    }

}
